---
title: "Swift 中的函數和閉包"
description: "學習如何在 Swift 中定義和使用函數和閉包，與 JavaScript 進行直接對比。"
---

## 1. 介紹

函數和閉包是 JavaScript 和 Swift 中的基本構建塊。雖然 JavaScript 將函數視為一等公民，但 Swift 帶來了額外的安全性、清晰性和表達能力，特別是在類型系統和閉包語法方面。

**主要學習目標：**
- 理解 Swift 中的函數宣告和用法
- 對比參數處理和返回值
- 學習閉包語法和用法
- 探索高階函數和函數式程式設計模式

## 2. 函數宣告和參數

### 2.1 基本函數宣告

<UniversalEditor title="函數宣告對比" compare={true}>
```javascript !! js
// JavaScript 函數宣告
function greet(name) {
    return `Hello, ${name}!`;
}

// 箭頭函數
const greetArrow = (name) => `Hello, ${name}!`;

console.log(greet("Alice"));
console.log(greetArrow("Bob"));
```

```swift !! swift
// Swift 函數宣告
func greet(name: String) -> String {
    return "Hello, \(name)!"
}

print(greet(name: "Alice"))
```
</UniversalEditor>

### 2.2 參數和返回值

Swift 需要顯式的參數類型和返回類型，使程式碼更安全、更可預測。

<UniversalEditor title="參數和返回值" compare={true}>
```javascript !! js
// JavaScript - 參數和返回值
function add(a, b) {
    return a + b;
}

function sayHello() {
    return "Hello!";
}

console.log(add(2, 3));
console.log(sayHello());
```

```swift !! swift
// Swift - 參數和返回值
func add(a: Int, b: Int) -> Int {
    return a + b
}

func sayHello() -> String {
    return "Hello!"
}

print(add(a: 2, b: 3))
print(sayHello())
```
</UniversalEditor>

### 2.3 預設參數和可變參數

<UniversalEditor title="預設參數和可變參數" compare={true}>
```javascript !! js
// JavaScript 預設參數和剩餘參數
function greet(name = "World") {
    return `Hello, ${name}!`;
}

function sum(...numbers) {
    return numbers.reduce((acc, n) => acc + n, 0);
}

console.log(greet());
console.log(sum(1, 2, 3, 4));
```

```swift !! swift
// Swift 預設參數和可變參數
func greet(name: String = "World") -> String {
    return "Hello, \(name)!"
}

func sum(_ numbers: Int...) -> Int {
    return numbers.reduce(0, +)
}

print(greet())
print(sum(1, 2, 3, 4))
```
</UniversalEditor>

## 3. 閉包（匿名函數）

### 3.1 閉包語法基礎

Swift 中的閉包類似於 JavaScript 的匿名函數（lambda/箭頭函數），但語法更簡潔、更靈活。

<UniversalEditor title="閉包語法對比" compare={true}>
```javascript !! js
// JavaScript 匿名函數和箭頭函數
const add = function(a, b) {
    return a + b;
};

const multiply = (a, b) => a * b;

console.log(add(2, 3));
console.log(multiply(4, 5));
```

```swift !! swift
// Swift 閉包表達式
let add = { (a: Int, b: Int) -> Int in
    return a + b
}

let multiply: (Int, Int) -> Int = { a, b in a * b }

print(add(2, 3))
print(multiply(4, 5))
```
</UniversalEditor>

### 3.2 閉包簡寫語法

Swift 提供了幾種更簡潔的閉包寫法。

<UniversalEditor title="閉包簡寫語法" compare={true}>
```javascript !! js
// JavaScript 箭頭函數簡寫
const numbers = [1, 2, 3, 4, 5];

// 完整箭頭函數
const doubled = numbers.map((num) => {
    return num * 2;
});

// 簡寫箭頭函數
const tripled = numbers.map(num => num * 3);

// 更短的隱式返回
const squared = numbers.map(num => num ** 2);

console.log(doubled);
console.log(tripled);
console.log(squared);
```

```swift !! swift
// Swift 閉包簡寫語法
let numbers = [1, 2, 3, 4, 5]

// 完整閉包語法
let doubled = numbers.map({ (num: Int) -> Int in
    return num * 2
})

// 類型推斷
let tripled = numbers.map({ num in
    return num * 3
})

// 簡寫參數名
let squared = numbers.map({ $0 * $0 })

// 尾隨閉包語法
let filtered = numbers.filter { $0 > 2 }

print(doubled)
print(tripled)
print(squared)
print(filtered)
```
</UniversalEditor>

## 4. 高階函數

### 4.1 Map、Filter、Reduce

Swift 的集合方法與 JavaScript 的陣列方法類似，但具有更強的類型安全性。

<UniversalEditor title="高階函數" compare={true}>
```javascript !! js
// JavaScript 陣列方法
const numbers = [1, 2, 3, 4, 5, 6];

// Map
const doubled = numbers.map(num => num * 2);

// Filter
const evens = numbers.filter(num => num % 2 === 0);

// Reduce
const sum = numbers.reduce((acc, num) => acc + num, 0);

// 鏈式呼叫
const result = numbers
    .filter(num => num > 2)
    .map(num => num * 2)
    .reduce((acc, num) => acc + num, 0);

console.log(doubled);  // [2, 4, 6, 8, 10, 12]
console.log(evens);    // [2, 4, 6]
console.log(sum);      // 21
console.log(result);   // 30
```

```swift !! swift
// Swift 集合方法
let numbers = [1, 2, 3, 4, 5, 6]

// Map
let doubled = numbers.map { $0 * 2 }

// Filter
let evens = numbers.filter { $0 % 2 == 0 }

// Reduce
let sum = numbers.reduce(0, +)

// 鏈式呼叫
let result = numbers
    .filter { $0 > 2 }
    .map { $0 * 2 }
    .reduce(0, +)

print(doubled)  // [2, 4, 6, 8, 10, 12]
print(evens)    // [2, 4, 6]
print(sum)      // 21
print(result)   // 30
```
</UniversalEditor>

### 4.2 自訂高階函數

<UniversalEditor title="自訂高階函數" compare={true}>
```javascript !! js
// JavaScript 自訂高階函數
function forEach(array, callback) {
    for (let i = 0; i < array.length; i++) {
        callback(array[i], i, array);
    }
}

function find(array, predicate) {
    for (let item of array) {
        if (predicate(item)) {
            return item;
        }
    }
    return undefined;
}

const numbers = [1, 2, 3, 4, 5];

forEach(numbers, (num, index) => {
    console.log(`Number ${index}: ${num}`);
});

const firstEven = find(numbers, num => num % 2 === 0);
console.log(firstEven); // 2
```

```swift !! swift
// Swift 自訂高階函數
func forEach<T>(_ array: [T], _ callback: (T, Int) -> Void) {
    for (index, item) in array.enumerated() {
        callback(item, index)
    }
}

func find<T>(_ array: [T], _ predicate: (T) -> Bool) -> T? {
    for item in array {
        if predicate(item) {
            return item
        }
    }
    return nil
}

let numbers = [1, 2, 3, 4, 5]

forEach(numbers) { num, index in
    print("Number \(index): \(num)")
}

let firstEven = find(numbers) { $0 % 2 == 0 }
print(firstEven ?? "No even number found") // Optional(2)
```
</UniversalEditor>

## 5. 閉包捕獲和記憶體管理

### 5.1 閉包捕獲列表

Swift 的閉包捕獲列表提供了對值捕獲方式的顯式控制，與 JavaScript 的詞法作用域不同。

<UniversalEditor title="閉包捕獲列表" compare={true}>
```javascript !! js
// JavaScript 閉包捕獲（詞法作用域）
function createCounter() {
    let count = 0;
    
    return {
        increment: () => ++count,
        decrement: () => --count,
        getCount: () => count
    };
}

const counter = createCounter();
console.log(counter.getCount()); // 0
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
```

```swift !! swift
// Swift 閉包捕獲列表
func createCounter() -> (increment: () -> Int, decrement: () -> Int, getCount: () -> Int) {
    var count = 0
    
    let increment = { [count] in
        count += 1
        return count
    }
    
    let decrement = { [count] in
        count -= 1
        return count
    }
    
    let getCount = { count }
    
    return (increment: increment, decrement: decrement, getCount: getCount)
}

let counter = createCounter()
print(counter.getCount()) // 0
print(counter.increment()) // 1
print(counter.increment()) // 2
print(counter.decrement()) // 1
```
</UniversalEditor>

### 5.2 弱引用和無主引用

Swift 提供了對閉包中引用循環的顯式控制。

<UniversalEditor title="弱引用和無主引用" compare={true}>
```javascript !! js
// JavaScript - 不需要顯式弱引用
class Person {
    constructor(name) {
        this.name = name;
        this.friends = [];
    }
    
    addFriend(friend) {
        this.friends.push(friend);
        friend.friends.push(this);
    }
    
    introduce() {
        console.log(`Hi, I'm ${this.name}`);
    }
}

const alice = new Person("Alice");
const bob = new Person("Bob");
alice.addFriend(bob);
```

```swift !! swift
// Swift - 顯式弱引用防止循環引用
class Person {
    let name: String
    var friends: [Person] = []
    
    init(name: String) {
        self.name = name
    }
    
    func addFriend(_ friend: Person) {
        friends.append(friend)
        friend.friends.append(self)
    }
    
    func introduce() {
        print("Hi, I'm \(name)")
    }
    
    // 使用弱引用防止循環引用
    lazy var introduceClosure: () -> Void = { [weak self] in
        guard let self = self else { return }
        print("Hi, I'm \(self.name)")
    }
}

let alice = Person(name: "Alice")
let bob = Person(name: "Bob")
alice.addFriend(bob)
```
</UniversalEditor>

## 6. 函數式程式設計模式

### 6.1 函數組合

<UniversalEditor title="函數組合" compare={true}>
```javascript !! js
// JavaScript 函數組合
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x ** 2;

const composed = compose(square, double, addOne);
const piped = pipe(addOne, double, square);

console.log(composed(3)); // ((3 + 1) * 2) ^ 2 = 64
console.log(piped(3));    // ((3 + 1) * 2) ^ 2 = 64
```

```swift !! swift
// Swift 函數組合
func compose<T, U, V>(_ f: @escaping (U) -> V, _ g: @escaping (T) -> U) -> (T) -> V {
    return { f(g($0)) }
}

func pipe<T, U, V>(_ f: @escaping (T) -> U, _ g: @escaping (U) -> V) -> (T) -> V {
    return { g(f($0)) }
}

let addOne: (Int) -> Int = { $0 + 1 }
let double: (Int) -> Int = { $0 * 2 }
let square: (Int) -> Int = { $0 * $0 }

let composed = compose(square, compose(double, addOne))
let piped = pipe(addOne, pipe(double, square))

print(composed(3)) // ((3 + 1) * 2) ^ 2 = 64
print(piped(3))    // ((3 + 1) * 2) ^ 2 = 64
```
</UniversalEditor>

### 6.2 柯里化和部分應用

<UniversalEditor title="柯里化和部分應用" compare={true}>
```javascript !! js
// JavaScript 柯里化
const add = (a) => (b) => a + b;
const addFive = add(5);

console.log(addFive(3)); // 8

// 部分應用
const multiply = (a, b) => a * b;
const multiplyByTwo = multiply.bind(null, 2);

console.log(multiplyByTwo(4)); // 8
```

```swift !! swift
// Swift 柯里化
func add(_ a: Int) -> (Int) -> Int {
    return { b in a + b }
}

let addFive = add(5)
print(addFive(3)) // 8

// 使用閉包的部分應用
func multiply(_ a: Int, _ b: Int) -> Int {
    return a * b
}

let multiplyByTwo: (Int) -> Int = { multiply(2, $0) }
print(multiplyByTwo(4)) // 8
```
</UniversalEditor>

## 7. 練習

### 練習 1: 基本函數和閉包

<UniversalEditor title="練習 1: 基本函數和閉包" compare={true}>
```javascript !! js
// JavaScript 練習
function calculateArea(width, height) {
    return width * height;
}

const calculatePerimeter = (width, height) => 2 * (width + height);

function createGreeter(greeting) {
    return function(name) {
        return `${greeting}, ${name}!`;
    };
}

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 過濾偶數並翻倍
const result = numbers
    .filter(num => num % 2 === 0)
    .map(num => num * 2)
    .reduce((sum, num) => sum + num, 0);

console.log(calculateArea(5, 3)); // 15
console.log(calculatePerimeter(5, 3)); // 16

const helloGreeter = createGreeter("Hello");
console.log(helloGreeter("Alice")); // "Hello, Alice!"

console.log(result); // 60 (2+4+6+8+10) * 2
```

```swift !! swift
// Swift 練習
func calculateArea(width: Int, height: Int) -> Int {
    return width * height
}

let calculatePerimeter: (Int, Int) -> Int = { width, height in
    2 * (width + height)
}

func createGreeter(greeting: String) -> (String) -> String {
    return { name in
        "\(greeting), \(name)!"
    }
}

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 過濾偶數並翻倍
let result = numbers
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }
    .reduce(0, +)

print(calculateArea(width: 5, height: 3)) // 15
print(calculatePerimeter(5, 3)) // 16

let helloGreeter = createGreeter(greeting: "Hello")
print(helloGreeter("Alice")) // "Hello, Alice!"

print(result) // 60 (2+4+6+8+10) * 2
```
</UniversalEditor>

### 練習 2: 高級閉包用法

<UniversalEditor title="練習 2: 高級閉包用法" compare={true}>
```javascript !! js
// JavaScript 高級閉包練習
function createCounter(initialValue = 0) {
    let count = initialValue;
    
    return {
        increment: () => ++count,
        decrement: () => --count,
        reset: () => { count = initialValue; return count; },
        getValue: () => count
    };
}

function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

const expensiveFunction = memoize((n) => {
    console.log(`Computing for ${n}`);
    return n * n;
});

const counter = createCounter(10);
console.log(counter.getValue()); // 10
console.log(counter.increment()); // 11
console.log(counter.increment()); // 12
console.log(counter.reset()); // 10

console.log(expensiveFunction(5)); // Computing for 5, 25
console.log(expensiveFunction(5)); // 25 (cached)
```

```swift !! swift
// Swift 高級閉包練習
func createCounter(initialValue: Int = 0) -> (increment: () -> Int, decrement: () -> Int, reset: () -> Int, getValue: () -> Int) {
    var count = initialValue
    
    let increment = { [count] in
        count += 1
        return count
    }
    
    let decrement = { [count] in
        count -= 1
        return count
    }
    
    let reset = { [initialValue] in
        count = initialValue
        return count
    }
    
    let getValue = { count }
    
    return (increment: increment, decrement: decrement, reset: reset, getValue: getValue)
}

func memoize<T: Hashable, U>(_ fn: @escaping (T) -> U) -> (T) -> U {
    var cache: [T: U] = [:]
    
    return { input in
        if let cached = cache[input] {
            return cached
        }
        let result = fn(input)
        cache[input] = result
        return result
    }
}

let expensiveFunction = memoize { (n: Int) -> Int in
    print("Computing for \(n)")
    return n * n
}

let counter = createCounter(initialValue: 10)
print(counter.getValue()) // 10
print(counter.increment()) // 11
print(counter.increment()) // 12
print(counter.reset()) // 10

print(expensiveFunction(5)) // Computing for 5, 25
print(expensiveFunction(5)) // 25 (cached)
```
</UniversalEditor>

## 8. 關鍵要點

### 8.1 函數和閉包差異

| 特性 | JavaScript | Swift | 關鍵差異 |
|---|---|---|---|
| **函數宣告** | `function` 關鍵字或箭頭函數 | `func` 關鍵字 | Swift 需要顯式類型 |
| **參數類型** | 動態類型 | 靜態類型 | Swift 提供編譯時安全性 |
| **返回類型** | 推斷 | 顯式 | Swift 需要返回類型宣告 |
| **閉包語法** | 箭頭函數 `=>` | `{ } in` 語法 | Swift 有更靈活的語法選項 |
| **捕獲列表** | 詞法作用域 | 顯式捕獲列表 | Swift 提供更多記憶體控制 |
| **高階函數** | 陣列方法 | 集合方法 | 相似功能，不同語法 |

### 8.2 最佳實踐

1. **使用類型註解**: 顯式類型使程式碼更清晰、更安全
2. **優先使用尾隨閉包**: 使用尾隨閉包語法提高可讀性
3. **使用捕獲列表**: 顯式控制閉包捕獲以防止循環引用
4. **利用類型推斷**: 當明顯時讓 Swift 推斷類型
5. **使用函數式模式**: 利用 Swift 的函數式程式設計特性
6. **處理記憶體管理**: 適當使用弱引用和無主引用

### 8.3 常見陷阱

1. **忘記參數標籤**: Swift 使用外部參數名
2. **忽略返回類型**: Swift 需要顯式返回類型宣告
3. **循環引用**: 在捕獲 self 的閉包中不使用弱引用
4. **類型不匹配**: 不匹配閉包類型與期望的函數類型
5. **過度複雜化閉包**: 在簡寫更清晰時使用完整語法

## 9. 下一步

在下一個模組中，我們將探索 Swift 中的集合，包括：
- 陣列、字典和集合
- 集合方法和操作
- 自訂集合類型
- 性能考慮

這個函數和閉包基礎將為你準備更高級的 Swift 程式設計概念和 iOS 開發。 